/**
   @file serveur/theatre/lecteur_scenario.hpp
   @brief Sert à lire un scénario.
*/

#ifndef LECTEUR_DEFINI
/**
   @brief Empêche les inclusions multiples.
*/
#define LECTEUR_DEFINI

#include <QObject>
#include <QString>
#include <QList>
#include <QStringList>
#include <QSharedPointer>
#include "protocole.hpp"

/**
   @brief La classe d'événements.
   
   Elle contient son type, de type Evenement::Type (ça fait beaucoup
   de type), et le message, de type Protocole::Message. Ces deux
   attributs sont constants. On peut déclarer un événement via le
   premier constructeur 
   (Evenement::Evenement(enum Type, const Protocole::Message &)),
   via le constructeur de copie
   (Evenement::Evenement(const Evenement &)), ou grâce à l'une des
   deux méthodes statiques Evenement::ClientDit ou
   Evenement::ServeurDit.
*/
class Evenement
{
public:
  /**
     @brief Le type d'événement.
  */
  enum Type
    {
      /**
	 @brief C'est le client qui doit envoyer un message.
      */
      CLIENT_DIT,
      /**
	 @brief C'est le serveur qui doit envoyer un message.
      */
      SERVEUR_DIT
    };
  /**
     @brief Constructeur principal.
       
     @param type La direction du message.
     @param message Le message en question.
     @see Evenement::ClientDit
     @see Evenement::ServeurDit
  */
  Evenement(enum Type type, const Protocole::Message & message);
  /**
     @brief Constructeur futé.

     Construit un événement à partir d'une phrase telle que définie
     dans le fichier theatre/echapper.hpp. On suppose que les
     arguments sont bons.
       
     @param phrase La phrase à examiner. Elle respecte les
     spécifications de la valeur de retour de rassembler().
  */
  Evenement(const QStringList & phrase);
  /**
     @brief Constructeur de copie.
       
     @param e Un événement valide, construit avec
     Evenement::Evenement(enum Type, const Protocole::Message &) ou
     l'une des méthodes statiques Evenement::ClientDit ou
     Evenement::ServeurDit. 
  */
  Evenement(const Evenement & e);
  /**
     @brief Indique le Evenement::Type de l'événement.

     @return Le type.
     @see Evenement::CLIENT_DIT
     @see Evenement::SERVEUR_DIT
  */
  enum Type type() const;
  /**
     @brief Retourne une copie du message.

     @return Le message.
  */
  Protocole::Message message() const;
  /**
     @brief Construit de façon simple un événement à attendre.

     @param message Le message à attendre.
     @return Un nouvel événement.
  */
  static Evenement ClientDit(const Protocole::Message & message);
  /**
     @brief Construit de façon simple un événement à déclencher.

     @param message Le message à envoyer.
     @return un nouvel événement.
  */
  static Evenement ServeurDit(const Protocole::Message & message);
  /**
     @brief Moteur de la conversion phrase -> événement.

     Construit de façon simple un événement en analysant la
     phrase. Le type est déduit, le message déduit est
     automatiquement compris.

     @see Evenement(const QStringList &)
  */
  static Evenement parser_phrase(const QStringList & phrase);
private:
  const enum Type m_type;
  const Protocole::Message m_message;
};

/**
   @brief Classe pour suivre un scénario.

   La classe LecteurScenario lit un fichier de scénario puis suit les
   instructions, qui sont :
   - soit d'attendre un certain message de la part du client,
   - soit d'envoyer un message précis au client.

   En cas de non conformité, un signal LecteurScenario::erreur() est
   émis. À la fin du scénario, si le client émet un autre message, le
   signal LecteurScenario::hors_scenario() est émis.
*/
class LecteurScenario: public QObject
{
  Q_OBJECT;
public:
  /**
     @brief Constructeur par défaut.

     Attention : le fichier n'est pas lu. Le scénario est
     (ré)initialisé lors de l'appel de
     LecteurScenario::lire_scenario(). 

     @param parent L'objet parent au sens de Qt.
  */
  LecteurScenario(QObject * parent = 0);
  /**
     @brief Sélectionne le scénario à suivre.

     @param contenu_fichier Le contenu du fichier. Il est interprété à
     ce moment-là.
     @param ignored La liste des mots ignorés.
  */
  void lire_scenario(const QString & contenu_fichier, 
		     QStringList & ignored);
public slots:
  /**
     @brief À appeler lorsque le client envoie un message.

     Le message est mis en attente pour être traité lorsque le
     scénario laissera la parole au client. Si le message est accepté,
     le signal LecteurScenario::etape_suivante() est émis.

     @param m Le message envoyé.
  */
  void recevoir_message(Protocole::Message m);
signals:
  /**
     @brief Émis lorsqu'on doit envoyer un message au client.

     Juste après, le signal LecteurScenario::etape_suivante() est
     émis.

     @param m Le message qu'il faut émettre.
  */
  void doit_emettre(Protocole::Message m);
  /**
     @brief Émis lorsque le client n'a pas émis le bon message.

     Dans ce cas, le scénario s'arrête et les prochains messages reçus
     feront émettre LecteurScenario::hors_scenario().

     @param recu Le message effectivement reçu.
     @param attendu Le message qui était attendu.
  */
  void erreur(Protocole::Message recu, Protocole::Message attendu);
  /**
     @brief Émis lorsque le client envoie un message hors du
     scénario. 

     Le scénario est considéré comme terminé lorsqu'une erreur s'est
     produite ou que la dernière étape spécifiée a été traitée.

     @param recu Le message reçu alors qu'il n'aurait pas dû l'être. 
  */
  void hors_scenario(Protocole::Message recu);
private slots:
  /**
     @brief Passe à l'étape suivante.

     Pas nécessairement un slot, mais c'est psychologique.
  */
  void avancer();
private:
  QList<Protocole::Message> repliques_client; //en attente
  QList<QSharedPointer<Evenement> > scenario; //restant
};

#endif
